﻿#ifndef UDP_ACK_CLIENT_H
#define UDP_ACK_CLIENT_H

#include "tftp.h"

#include <QObject>
#include <QList>
#include <QtNetwork>

namespace TFTP {

class ClientWriteSession;
class ClientReadSession;
/**
* @brief 实现客户端，以接收并发送文件。
*/
class UdpAckClient : public QObject
{
    Q_OBJECT
public:
    /**
    * @brief 构造函数
    * @param addr 要连接到的服务器的地址
    * @param port 要连接到的服务器端口
    * @note 应该为要连接到的每台服务器创建一个客户端
    */
    UdpAckClient(const QHostAddress &addr
           , quint16  port);
    ~UdpAckClient();

    /**
    * @brief 启动到服务器的上载传输
    * @param localname 本地文件名
    * @param remotename 远程端文件名
    * @note 对于客户端，读和写的概念是颠倒的：一端用读会话，另一方端用就用写会话！
    */
    void putFile(const QString &localname
                 , const QString &remotename
                 , TransferMode trmode = Octet);

    /**
    * @brief 启动下载传输到服务器
    * @param localname 本地文件名
    * @param remotename 远程端文件名
    * @note 对于客户端，读和写的概念是颠倒的：一端用读会话，
    * 另一端总是用写会话！
    */
    void getFile(const QString &localname
                 , const QString &remotename
                 , TransferMode trmode = Octet);
signals:

    /**
    * @brief 接收文件已完成
    * @param dev 写入文件的QIODevice实例
    * @param remotefilename 远程端文件的名称
    *   该信号在文件接收完成时发出
    */
    void receivedFile(QIODevice *dev, const QString remotefilename);

    /**
    * @brief 发送文件完成
    * @param dev  读取文件的QIODevice实例
    * @param remotefilename 远程端文件的名称
    *   该信号在文件发送完成时发出
    */
    void sentFile(QIODevice *dev, QString remotefilename);

protected slots:
    /**
     * @brief 从套接字接收数据时调用
     */
    void dataReceived();

protected:
    /**
    * @brief 查找包含提供的TransferInfo数据的写入实例
    * @param ti 要查找的写入实例的TransferInfo数据
    * @return
    * 指向写入会话的指针 如果不到
    * nullptr 如果找不到
    */
    ClientWriteSession *findWSession(const TransferInfo &ti);

    /**
    * @brief 查找包含提供的TransferInfo数据的读取实例
    * @param ti 要查找的读取实例的TransferInfo数据
    * @return
    * 指向读取会话的指针 如果不到
    * nullptr 如果找不到
    */
    ClientReadSession *findRSession(const TransferInfo &ti);

protected:
    QList<ClientWriteSession*>    writes;     ///< 所有打开写入实例的列表
    QList<ClientReadSession*>     reads;      ///< 所有打开的读取实例列表
    QUdpSocket        * socket;               ///< UDP套接字
    QHostAddress        address;              ///< 目标地址
    quint16             port;                 ///< 目标端口

};

};

#endif
